package com.google.code.jholidays.io;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;


import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

import org.junit.Test;

import com.google.code.jholidays.core.DescriptorCollection;
import com.google.code.jholidays.core.EventDescriptor;
import com.google.code.jholidays.io.csv.CsvReader;


public class CsvReaderTest {
    @Test
    public void testCreateSourceFileExists() throws IOException {

	File f = createAndFillTempFile();
	final int recordsCount = getLinesCount(f.getAbsolutePath());

	CsvReader reader = new CsvReader(f.getAbsolutePath(), ";");
	DescriptorCollection collection = reader.read();

	assertNotNull(collection);
	assertEquals(recordsCount, collection.size());
    }

    @Test
    public void testCreateSourceFileDoesntExist() throws IOException {
	String fileName = "$$::unknown//://12345";
	CsvReader reader = new CsvReader(fileName, ";");

	File f = new File(fileName);
	assertFalse("File mustn't exist", f.exists());

	DescriptorCollection collection = reader.read();
	assertNull(collection);
    }

    @Test
    public void testReadValidData() throws IOException {
	File file = File.createTempFile("tmp", "000");
	file.deleteOnExit();

	final int recordsCount = 25;
	final String delimiter = ";";

	// create list of descriptors
	DescriptorCollection list = new DescriptorCollection();
	int median = recordsCount / 2;

	for (int i = 0; i < recordsCount; ++i) {
	    String name = "name" + Integer.toString(i);
	    String description = "description" + Integer.toString(i);
	    String expression = "expression" + Integer.toString(i);
	    int parentID = 0;

	    if (i > median)
		parentID = i - 1;

	    EventDescriptor descriptor = create(i, name, description, parentID,
		    expression);
	    assertNotNull(descriptor);
	    assertTrue(list.add(descriptor));
	}
	assertEquals(recordsCount, list.size());

	// save list into file
	BufferedWriter writer = new BufferedWriter(new FileWriter(file));

	for (int i = 0; i < recordsCount; ++i) {
	    EventDescriptor descriptor = list.get(i);
	    assertNotNull(descriptor);

	    StringBuilder sb = new StringBuilder();
	    sb.append(descriptor.getID());
	    sb.append(delimiter);
	    sb.append(descriptor.getName());
	    sb.append(delimiter);
	    sb.append(descriptor.getDescription());
	    sb.append(delimiter);
	    if (descriptor.getParentID() != 0)
		sb.append(descriptor.getParentID());
	    sb.append(delimiter);
	    sb.append(descriptor.getExpression());
	    sb.append('\n');

	    // System.out.println(sb);

	    writer.write(sb.toString());
	}

	writer.close();

	// validate written file manually
	CsvReader reader = new CsvReader(file.getAbsolutePath(), delimiter);
	DescriptorCollection collection = reader.read();

	assertNotNull(collection);
	assertEquals(list.size(), collection.size());

	// compare lists
	for (int i = 0; i < list.size(); ++i) {
	    EventDescriptor ethalon = list.get(i);
	    EventDescriptor loaded = collection.get(i);

	    assertNotNull(ethalon);
	    assertNotNull(loaded);

	    assertEquals(ethalon.getID(), loaded.getID());
	    assertEquals(ethalon.getName(), loaded.getName());
	    assertEquals(ethalon.getDescription(), loaded.getDescription());
	    assertEquals(ethalon.getParentID(), loaded.getParentID());
	    assertEquals(ethalon.getExpression(), loaded.getExpression());
	}
    }

    @Test
    public void testFailReadInvalidData() throws IOException {
	File file = File.createTempFile("tmp", "000");
	file.deleteOnExit();

	final int recordsCount = 25;
	final String delimiter = ";";

	// create list of descriptors
	DescriptorCollection list = new DescriptorCollection();
	int median = recordsCount / 2;

	for (int i = 0; i < recordsCount; ++i) {
	    String name = "name" + Integer.toString(i);
	    String description = "description" + Integer.toString(i);
	    String expression = "expression" + Integer.toString(i);
	    int parentID = 0;

	    if (i > median)
		parentID = i - 1;

	    EventDescriptor descriptor = create(i, name, description, parentID,
		    expression);
	    assertNotNull(descriptor);
	    assertTrue(list.add(descriptor));
	}
	assertEquals(recordsCount, list.size());

	// save list into file
	BufferedWriter writer = new BufferedWriter(new FileWriter(file));

	for (int i = 0; i < recordsCount; ++i) {
	    EventDescriptor descriptor = list.get(i);
	    assertNotNull(descriptor);

	    StringBuilder sb = new StringBuilder();
	    sb.append(descriptor.getID());
	    sb.append(delimiter);
	    sb.append(descriptor.getName());
	    sb.append(delimiter);
	    sb.append(descriptor.getDescription());
	    sb.append(delimiter);
	    if (descriptor.getParentID() != 0)
		sb.append(descriptor.getParentID());
	    sb.append(delimiter);
	    sb.append(descriptor.getExpression());
	    sb.append('\n');

	    // System.out.println(sb);

	    writer.write(sb.toString());
	}

	// write mailformed string
	writer.write("malformed string");

	writer.close();

	// validate written file
	CsvReader reader = new CsvReader(file.getAbsolutePath(), delimiter);
	DescriptorCollection collection = reader.read();

	assertNull(collection);
    }

    protected EventDescriptor create(int id, String name, String description,
	    int parentID, String expression) {
	EventDescriptor result = new EventDescriptor(id, parentID);
	result.setName(name);
	result.setDescription(description);
	result.setExpression(expression);

	return result;
    }

    protected File createAndFillTempFile() throws IOException {
	File file = File.createTempFile("tmp", "000");
	file.deleteOnExit();

	// fill file with data
	FileWriter writer = new FileWriter(file);
	writer.write("1;first;none;;none" + '\n');
	writer.write("2;second;none;1;none" + '\n');
	writer.write("3;third;none;2;none" + '\n');
	writer.write("4;fourth;none;2;none" + '\n');

	writer.close();

	return file;
    }

    protected int getLinesCount(String fileName) {
	int result = 0;

	File file = new File(fileName);

	if (file.exists()) {
	    BufferedReader reader = null;
	    try {
		reader = new BufferedReader(new FileReader(file));
		while (reader.readLine() != null)
		    ++result;
	    } catch (Exception e) {
		result = 0;
	    } finally {
		if (reader != null)
		    try {
			reader.close();
		    } catch (IOException e) {
			// nothing todo here
		    }
	    }
	}

	return result;
    }
}
